package antrace;

import java.util.Random;

public class TongueGameLayer extends AbstractGameLayer {

	public static int MAX_TONGUE_LENGTH = 20;
	public static final int TUNNEL = 0;

	/** Återvändsgränd */
	public static final int DEAD_END = -1;
	
	/* Håll reda på vilken riktning som är vilken enligt:
	 *     0
	 *     ^
	 *     |
	 * 3 <-+-> 1
	 *     |
	 *     ˇ
	 *     2
	 */
	public static final int UP = 0;
	public static final int DOWN = 2;
	public static final int RIGHT = 1;
	public static final int LEFT = 3;

	/* Konstanter för index till tungans tiles. Namngivna map riktning åt höger */

	public static final int TONGUE_RIGHT_TO_DOWN = 1; // UP_TO_LEFT
	public static final int TONGUE_RIGHT_TO_UP = 2; // DOWN_TO_LEFT
	public static final int TONGUE_DOWN_TO_LEFT = 3; // -
	public static final int TONGUE_DOWN_TO_RIGHT = 4;

	public static final int TONGUE_UP_TO_RIGHT = 5;
	public static final int TONGUE_UP_TO_LEFT = 6;
	public static final int TONGUE_LEFT_TO_UP = 7;
	public static final int TONGUE_LEFT_TO_DOWN = 8;

	public static final int TONGUE_RIGHT_CURVE_UP = 9;
	public static final int TONGUE_RIGHT_CURVE_DOWN = 10;
	public static final int TONGUE_RIGHT_STRAIGHT = 11;
	public static final int TONGUE_RIGHT_TIP_DOWN = 12;
	public static final int TONGUE_RIGHT_TIP_UP = 13;

	public static final int TONGUE_UP_CURVE_UP = 14;
	public static final int TONGUE_UP_CURVE_DOWN = 15;
	public static final int TONGUE_UP_STRAIGHT = 16;
	public static final int TONGUE_UP_TIP_DOWN = 17;
	public static final int TONGUE_UP_TIP_UP = 18;

	public static final int TONGUE_DOWN_CURVE_UP = 19;
	public static final int TONGUE_DOWN_CURVE_DOWN = 20;
	public static final int TONGUE_DOWN_STRAIGHT = 21;
	public static final int TONGUE_DOWN_TIP_DOWN = 22;
	public static final int TONGUE_DOWN_TIP_UP = 23;

	public static final int TONGUE_LEFT_CURVE_UP = 24;
	public static final int TONGUE_LEFT_CURVE_DOWN = 25;
	public static final int TONGUE_LEFT_STRAIGHT = 26;
	public static final int TONGUE_LEFT_TIP_DOWN = 27;
	public static final int TONGUE_LEFT_TIP_UP = 28;
	
	/* Olika environments 2^4 = 16 st, dvs alla möjliga alternativ av korsningar */
	public static final int[] ENVIRONMENT_1 = {1, 0, 1, 1};
	public static final int[] ENVIRONMENT_2 = {1, 0, 1, 0};
	public static final int[] ENVIRONMENT_3 = {0, 0, 1, 1};
	public static final int[] ENVIRONMENT_4 = {1, 0, 0, 1};
	public static final int[] ENVIRONMENT_5 = {1, 0, 0, 0};
	public static final int[] ENVIRONMENT_6 = {0, 0, 1, 0};
	public static final int[] ENVIRONMENT_7 = {0, 0, 0, 1};
	public static final int[] ENVIRONMENT_8 = {0, 0, 0, 0};
	public static final int[] ENVIRONMENT_9 = {1, 1, 1, 1};
	public static final int[] ENVIRONMENT_10 = {1, 1, 1, 0};
	public static final int[] ENVIRONMENT_11 = {0, 1, 1, 1};
	public static final int[] ENVIRONMENT_12 = {1, 1, 0, 1};
	public static final int[] ENVIRONMENT_13 = {1, 1, 0, 0};
	public static final int[] ENVIRONMENT_14 = {0, 1, 1, 0};
	public static final int[] ENVIRONMENT_15 = {0, 1, 0, 1};
	public static final int[] ENVIRONMENT_16 = {0, 1, 0, 0};
	
	/** Tungans längd i tiles, max 12 (skärmens längd). Rimlig längd är kanske 8 om,
	 * tungan kommer in rakt, längre än så om tungan letar sig genom gångarna */
	private int tongueLength;
	private int currentMaxLength;
	/** Position x-led i lagret (vänstersystem) */
	private int x;
	/** Position y-led i lagret (vänstersystem) */
	private int y;
	/** Riktningen tungan är på väg åt för tillfället */
	private int direction;
	/** Riktning i föregående tile*/
	private int lastDirection;

	/** boolean för om tungan är på väg ut eller inte */
	private boolean launchTongue;
	/** Random-objekt för att kunna slumpa: 
	 * 		* tiles för tungan
	 * 		* vägval i korsningar 
	 * 		* startposition för tungan */
	private Random random;
	/** Den rad där tungan kommer in */
	private int startRow;
	/** Referens till jordlagret. Tungan måste kunna kolla vad som är tunnlar och inte. */
	private DiggableGameLayer diggable;
	private AntSprite ant;
	private int startColumnX;
	/** Bredd i tiles på lagret */
	static int blockWidth = 16;
	
	int counter = 0;
	int tongueFrequencyCounter = 0;
	private int[] tongueCrawlX = new int[MAX_TONGUE_LENGTH+1];
	private int[] tongueCrawlY = new int[MAX_TONGUE_LENGTH+1];
	private int[] tongueCrawlDir = new int[MAX_TONGUE_LENGTH+1];

	public TongueGameLayer() {

		// bredd, höjd (i tiles)
		super(blockWidth, 12,  AntRace.midlet.loadImage("/images/tiles/tonguetiles2.png").image, 20, 20);

		launchTongue = false;
		direction = lastDirection = RIGHT;
		tongueLength = 0;
		random = new Random();

		byte[] cells = {
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
		};

		startColumnX = 0;
		createTiledLayerFromArray(cells, blockWidth);
		setPosition(0, 80);
		currentMaxLength = 8;
	}

	public TongueGameLayer(DiggableGameLayer diggable) {
		this();
		this.diggable = diggable;
	}
	
	public TongueGameLayer(DiggableGameLayer diggable, AntSprite ant) {
		this(diggable);
		this.ant = ant;
	}
	
	/** Sätt alla tiles till 0 (ingen tunga) */
	public void resetTongue() {
		//   col, row, numCols, numRows, tileIndex
		fillCells(0, 0, 12, 12, 0);
		direction = lastDirection = RIGHT;
		x = 0;
		y = 0;
	}

	/** När tungan träffar myran, DÖDA MYRAN! */
	public void collide(AntSprite ant) {
		ant.kill();
		AntRace.game.setLastFrame();
		AntRace.game.gameoverMenu.setTimer((int)AntRace.getCentralTime());
		AntRace.game.gameoverMenu.toggleVisible();
	}

	/**
	 * Öka längden på tungan med en tile
	 */
	private void increaseLength() {
		tongueLength++;
		if (tongueLength > currentMaxLength) {
			tongueLength = currentMaxLength;
			launchTongue = false;
		}
		switch (direction) {
		case RIGHT:
			x++;
			break;
		case LEFT:
			x--;
			break;
		case UP:
			y--;
			break;
		case DOWN:
			y++;
			break;
		}
		x = x > 12 ? 12 : x;
		y = y > 12 ? 12 : y;
		x = x < 0 ? 0 : x;
		y = y < 0 ? 0 : y;
	}

	/**
	 * Minska längden på tungan med en tile
	 */
	private void decreaseLength() {
		tongueLength--;
		if (tongueLength < 0) {
			tongueLength = 0;
		}
		switch (direction) {
		case RIGHT:
			x++;
			break;
		case LEFT:
			x--;
			break;
		case UP:
			y--;
			break;
		case DOWN:
			y++;
			break;
		}
		x = x < 0 ? 0 : x;
		y = y < 0 ? 0 : y;
		x = x > 12 ? 12 : x;
		y = y > 12 ? 12 : y;
	}

	/** Skjuter iväg tungan på slumpad rad där tunnel finns (OH NO! NOT THE GIANT ANT EATER!) */
	public void launch() {
		if (tongueLength > 0) {
			return;
		}
		resetTongue();
		launchTongue = true;
		int[] startPositions = diggable.getStartColumn();
		startRow = startPositions[random.nextInt(startPositions.length)];
		x = 0;
		y = startRow;
		tongueLength = 0;
		
		tongueCrawlX[0] = x;
		tongueCrawlY[0] = y;
		tongueCrawlDir[0] = RIGHT;

		startColumnX = (int)Math.floor((ant.getWorldX()-110)/20)-2;
		this.setPosition(startColumnX*20+diggable.getX(),80);

	}

	/** 
	 * Uppdatera tungan för varje frame.
	 * Använd antingen moveStraight() för att tungan kommer rakt in från vänster,
	 * eller moveInTunnels() för att tungan ska leta sig genom gångarna.
	 * @see moveInTunnels
	 */
	public void update() {
		tongueFrequencyCounter++;
		if (tongueFrequencyCounter % 2 == 0) {
			moveInTunnels();
		}
	}

	public void reset() {
		this.setPosition(0, 80);
		this.resetTongue();
	}

//	public void scroll(int dx) {
//		setPosition(-(diggable.getX() % 20)-20, 80);
//	}

	private int[] combineEnvironments(int[] tunnel, int[] tongue) {
		int[] temp = new int[tunnel.length];
		for (int i = 0; i < tunnel.length; i++) {
			if (tunnel[i] == 1 && tongue[i] == 1) {
				temp[i] = 0;
			}
			else if (tunnel[i] == 0 && tongue[i] == 1) {
				temp[i] = 0;
			}
			else if (tunnel[i] == 1 && tongue[i] == 0) {
				temp[i] = 1;
			}
			else if (tunnel[i] == 0 && tongue[i] == 0) {
				temp[i] = 0;
			}
		}
		
		return temp;
	}
	
	/**
	 * Tungan letar sig genom gångarna och letar efter myran... 
	 */
	private void moveInTunnels() {
		if (launchTongue) {	// tungan på väg ut
			tongueCrawlX[tongueLength] = x;
			tongueCrawlY[tongueLength] = y;
			
			tongueCrawlDir[tongueLength] = direction;
			int[] tunnels = diggable.getGridEnvironment(x + startColumnX, y);
			int[] tongue = this.getTongueEnvironment();
			int[] environment = combineEnvironments(tunnels, tongue);
			lastDirection = direction;
			direction = chooseRoad(environment);
			if (direction == lastDirection) {
				setTongue();
			}
			else {
				setTurn();
			}
			increaseLength();
			
			setTip();

		}
		else if (!launchTongue && tongueLength > 0) { // tungan på väg tillbaka
			setCell(x, y, 0);
			direction = reverseDirection(tongueCrawlDir[tongueLength]);
			x = tongueCrawlX[tongueLength];
			y = tongueCrawlY[tongueLength];
			tongueLength--;
			setTipBackward(); // spetsen blir fel här på tillbakavägen... :(
		}
		else {
			resetTongue();
		}
	}

	/*
	 * Den här är inte testad och vet inte om den behövs...
	 * Ska iaf byta upp mot ner, vänster mot höger och vice versa.
	 */
	private int reverseDirection(int dir) {
		if (dir >= 2) {
			return (dir + 2) % 4;
		}
		else {
			return dir+2;
		}
	}
	
	/**
	 * Välj en av flera möjliga vägar i en korsning.
	 * (som inte är jord, sten eller tunga)
	 * @param environment information om intilliggande tiles (jord, tunnel, sten, tunga)
	 * i en array, arrayindex enligt: 
	 *   0
	 * 3 + 1
	 *   2
	 * @return den valda vägen. 0, 1, 2 eller 3. 
	 * Om återvändsgränd eller tunga åt alla håll returneras -1
	 */
	private int chooseRoad(int[] environment) {
		int randomSeed = 0;
		if (compareEnvironment(environment, ENVIRONMENT_1)) {
			randomSeed = random.nextInt(3);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
			else if (randomSeed == 2) {
				return LEFT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_2)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_3)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return LEFT;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_4)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return LEFT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_5)) {
			return UP;
		}
		else if (compareEnvironment(environment, ENVIRONMENT_6)) {
			return DOWN;
		}
		else if (compareEnvironment(environment, ENVIRONMENT_7)) {
			return LEFT;
		}
		else if (compareEnvironment(environment, ENVIRONMENT_8)) {
			launchTongue = false; // kan inte gå någonstans => vänd
			return LEFT;
		}
		else if (compareEnvironment(environment, ENVIRONMENT_9)) {
			randomSeed = random.nextInt(4);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
			if (randomSeed == 2) {
				return LEFT;
			}
			else if (randomSeed == 3) {
				return RIGHT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_10)) {
			randomSeed = random.nextInt(3);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
			else if (randomSeed == 2) {
				return RIGHT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_11)) {
			randomSeed = random.nextInt(3);
			
			if (randomSeed == 0) {
				return RIGHT;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
			else if (randomSeed == 2) {
				return LEFT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_12)) {
			randomSeed = random.nextInt(3);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return RIGHT;
			}
			else if (randomSeed == 2) {
				return LEFT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_13)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return UP;
			}
			else if (randomSeed == 1) {
				return RIGHT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_14)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return RIGHT;
			}
			else if (randomSeed == 1) {
				return DOWN;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_15)) {
			randomSeed = random.nextInt(2);
			
			if (randomSeed == 0) {
				return RIGHT;
			}
			else if (randomSeed == 1) {
				return LEFT;
			}
		}
		else if (compareEnvironment(environment, ENVIRONMENT_16)) {
			return RIGHT;
		}
		launchTongue = false;
		return LEFT;
	}
	
	/**
	 * Jämför två environments och ser om de är lika
	 * @param environment environment nr 1
	 * @param environment2 environment nr 1
	 * @return sant om de är lika, annars falskt
	 */
	private boolean compareEnvironment(int[] environment, int[] environment2) {
		for (int i = 0; i < environment.length; i++) {
			if (environment[i] != environment2[i]) {
				return false;
			}
		}
		return true;
	}

	/** Hämta tungans environment för att kunna 
	 * följa tungan tillbaka */
	private int[] getTongueEnvironment() {
		int[] environment = new int[4];
		if (y == 0) {
			environment[0] = 0;//getCell(x, y-1) > 0 ? 1 : 0;
		}
		else {
			environment[0] = getCell(x, y-1) > 0 ? 1 : 0;
		}
		
		if (x == 11) {
			environment[1] = 0;//getCell(x+1, y) > 0 ? 1 : 0;
		}
		else {
			environment[1] = getCell(x+1, y) > 0 ? 1 : 0;
		}
		
		if (y == 11) {
			environment[2] = 0;//getCell(x, y-1) > 0 ? 1 : 0;
		}
		else {
			environment[2] = getCell(x, y+1) > 0 ? 1 : 0;
		}
		
		if (x == 0) {
			environment[3] = 0;//getCell(x+1, y) > 0 ? 1 : 0;
		}
		else {
			environment[3] = getCell(x-1, y) > 0 ? 1 : 0;
		}
		
		return environment;
	}
	
	/**
	 * Sätt tungtile till tungspets på 
	 * aktuell position (x,y) beroende på riktning.
	 */
	private void setTip() {
		int tile = 0;
//		if (launchTongue) {
		switch (direction) {
		case LEFT:
			tile = randRange(TONGUE_LEFT_TIP_DOWN, TONGUE_LEFT_TIP_UP);
			break;
		case RIGHT:
			tile = randRange(TONGUE_RIGHT_TIP_DOWN, TONGUE_RIGHT_TIP_UP);
			break;
		case DOWN:
			tile = randRange(TONGUE_DOWN_TIP_DOWN, TONGUE_DOWN_TIP_UP);
			break;
		case UP:
			tile = randRange(TONGUE_UP_TIP_DOWN, TONGUE_UP_TIP_UP);
			break;
		}
	
		setCell(x, y, tile);
	}
	
	private void setTipBackward() {
		int tile = 0;
		switch (direction) {
		case LEFT:
			tile = randRange(TONGUE_RIGHT_TIP_DOWN, TONGUE_RIGHT_TIP_UP);
			break;
		case RIGHT:
			tile = randRange(TONGUE_LEFT_TIP_DOWN, TONGUE_LEFT_TIP_UP);
			break;
		case UP:
			tile = randRange(TONGUE_DOWN_TIP_DOWN, TONGUE_DOWN_TIP_UP);
			break;
		case DOWN:
			tile = randRange(TONGUE_UP_TIP_DOWN, TONGUE_UP_TIP_UP);
			break;
		}
		setCell(x, y, tile);
	}
	
	/**
	 * Sätt tungtile till vanlig tunga på 
	 * aktuell position (x,y) beroende på riktning.
	 */
	private void setTongue() {
		int tile = 0;
		switch (direction) {
		case LEFT:
			tile = randRange(TONGUE_LEFT_CURVE_UP, TONGUE_LEFT_STRAIGHT);
			break;
		case RIGHT:
			tile = randRange(TONGUE_RIGHT_CURVE_UP, TONGUE_RIGHT_STRAIGHT);
			break;
		case DOWN:
			tile = randRange(TONGUE_DOWN_CURVE_UP, TONGUE_DOWN_STRAIGHT);
			break;
		case UP:
			tile = randRange(TONGUE_UP_CURVE_UP, TONGUE_UP_STRAIGHT);
			break;
		}
		setCell(x, y, tile);
	}
	
	/**
	 * Om tungan har svängt i en korsning anropas 
	 * denna för att sätta rätt tile.
	 */
	private void setTurn() {
		int tile = 0;
		if (lastDirection == RIGHT && direction == UP) {
			tile = TONGUE_RIGHT_TO_UP;
		}
		else if (lastDirection == RIGHT && direction == DOWN) {
			tile = TONGUE_RIGHT_TO_DOWN;
		}
		else if (lastDirection == DOWN && direction == RIGHT) {
			tile = TONGUE_DOWN_TO_RIGHT;
		}
		else if (lastDirection == DOWN && direction == LEFT) {
			tile = TONGUE_DOWN_TO_LEFT;
		}
		else if (lastDirection == UP && direction == RIGHT) {
			tile = TONGUE_UP_TO_RIGHT;
		}
		else if (lastDirection == UP && direction == LEFT) {
			tile = TONGUE_UP_TO_LEFT;
		}
		else if (lastDirection == LEFT && direction == DOWN) {
			tile = TONGUE_LEFT_TO_DOWN;
		}
		else if (lastDirection == LEFT && direction == UP) {
			tile = TONGUE_LEFT_TO_UP;
		}
		setCell(x, y, tile);
	}
	
	/**
	 * Slumpa ett heltal i intervallet [a,b]
	 * @param a heltal a >= 0
	 * @param b heltal b > a
	 */
	private int randRange(int a, int b) {
		return a + random.nextInt(b+1-a);
	}
	
	public void increaseLength(int length){
		if (currentMaxLength + length <= MAX_TONGUE_LENGTH)
		currentMaxLength += length;
	}
	
	private boolean active() {
		return launchTongue && tongueLength == 0;
	}
	
}